(function(window, undefined){
    console.log("index/xSoftphone.js......")
	var PHONE_STATE = {
		INVALID: -1,
		IDLE: 0,
		RINGING: 1,
		ACCEPTED: 2,
		TERMINATED: 3
	};
	
	var SESSION_DIRECTION = {
		INVALID: -1,
		INBOUND: 0,
		OUTBOUND: 1
	};
    
    var SESSION_END_REASON = {
        NORMAL: "Normal",                   // 正常通话挂断
        CANCEL: "Cancel",                   // 外呼接通前主动挂断
        INVALID_NUMBER: "Invalid_number",   // 空号
        UNAVAILABLE: "Unavailable",         // 暂时无法接通
        BUSY: "Busy",                       // 用户忙，超时未接听
        REJECTED: "Rejected",               // 对方主动挂机，拒绝接听
        GATEWAY_ERROR: "Gateway_error",     // 落地网关错误（没接SIM卡、卡欠费、卡异常等）
        UNKNOWN: "Unknown"                  // 其它原因
    };

    function xSoftphone(options){
        this.STATE = {
            INVALID: PHONE_STATE.INVALID,
            IDLE: PHONE_STATE.IDLE,
            RINGING: PHONE_STATE.RINGING,
            ACCEPTED: PHONE_STATE.ACCEPTED,
            TERMINATED: PHONE_STATE.TERMINATED
        };

        this.DIRECTION = {
            INVALID: SESSION_DIRECTION.INVALID,
            INBOUND: SESSION_DIRECTION.INBOUND,
            OUTBOUND: SESSION_DIRECTION.OUTBOUND
        };

        var context = {
            state: PHONE_STATE.INVALID,
            direction: SESSION_DIRECTION.INVALID,
            session: null
        };

        var userAgent = new SIP.UA({
            uri: options.agentId + "@" + options.serverHost,
            transportOptions: {
                //wsServers: "ws://" + options.serverHost + ":" + options.serverPort,
                wsServers: "wss://" + options.serverHost + ":" + options.serverPort,
                traceSip: false
            },
            displayName: options.agentId,
            authorizationUser: options.agentId,
            password: options.password,
            autostart: false,
            register: true,
            log: {
                builtinEnabled: false,
                level: 0
            }
        });

        userAgent.on('registered', function(){
            console.log("agent registered");

            context.state = PHONE_STATE.IDLE;

            if (undefined != options["onStatus"]){
                options.onStatus(context.state, "", context.direction);
            }
        }).on('unregistered', function(response, cause){
            console.log("agent unregistered:" + "[" + cause + "]" + response);

            context.state = PHONE_STATE.INVALID;

            // null for positive response to un-REGISTER SIP request.
            if (null == cause){
                return;
            }

            if (undefined != options["onStatus"]){
                options.onStatus(context.state, "[" + cause + "]" + response, context.direction);
            }
        }).on('registrationFailed', function(response, cause){
            console.log("agent registrationFailed" + "[" + cause + "]" + response);

            context.state = PHONE_STATE.INVALID;

            if (undefined != options["onStatus"]){
                options.onStatus(context.state, "[" + cause + "]" + response, context.direction);
            }
        }).on('invite', function(s){
            console.log("incoming invite");

            if (null != context.session && SESSION_DIRECTION.INVALID != context.direction){
                console.log("There is already a session in progress, automatically rejecting new sessions!");

                s.reject();
                return;
            }

            context.session = s;
            context.session.progress();
            context.state = PHONE_STATE.RINGING;
            context.direction = SESSION_DIRECTION.INBOUND;
            addSessionEventListeners();

            if (undefined != options["onStatus"]){
                options.onStatus(context.state, context.session.remoteIdentity.displayName, context.direction);
            }
        });
		
        function addSessionEventListeners(){
            context.session.on('progress', function(response){
                console.log("session progress: " + response.reason_phrase + "[" + response.status_code + "]");

                context.state = PHONE_STATE.RINGING;

                if (undefined != options["onStatus"]){
                    options.onStatus(context.state, context.session.remoteIdentity.displayName, context.direction,response.status_code);
                }
            }).on('accepted', function(data){
                console.log("session accepted: " + data.reason_phrase + "[" + data.status_code + "]");

                context.state = PHONE_STATE.ACCEPTED;

                if (undefined != options["onStatus"]){
                    options.onStatus(context.state, "", context.direction,data.status_code);
                }
            }).on('rejected', function(response, cause){
                console.log("session rejected");
            }).on('failed', function(response, cause){
                if (SESSION_DIRECTION.INVALID == context.direction)
                    return;
                
                var reason = "";
                
                if (cause && SIP.C.causes.NOT_FOUND == cause){
                    reason = SESSION_END_REASON.INVALID_NUMBER;
                } else if (cause && SIP.C.causes.UNAVAILABLE == cause){
                    reason = SESSION_END_REASON.UNAVAILABLE;
                } else if (cause && SIP.C.causes.BUSY == cause){
                    reason = SESSION_END_REASON.BUSY;
                } else if (cause && SIP.C.causes.REJECTED == cause){
                    reason = SESSION_END_REASON.REJECTED;
                } else if (response && 503 == response.status_code){
                    reason = SESSION_END_REASON.GATEWAY_ERROR;
                } else if (response && 504 == response.status_code){
                    reason = SESSION_END_REASON.INVALID_NUMBER;
                } else if (response && 487 == response.status_code){
                    reason = SESSION_END_REASON.CANCEL;
                } else {
                    reason = SESSION_END_REASON.UNKNOWN;
                }
                
                console.log("session failed: " + reason);
                console.log("session failed: " + "[" + cause + "]" + (response ? response.reason_phrase : "unknown") + "[" + (response ? response.status_code : "unknown") + "]");

                context.state = PHONE_STATE.TERMINATED;

                if (undefined != options["onStatus"]){
                    options.onStatus(context.state, reason, context.direction);
                }
            }).on('bye', function(request){
                if (!request.hasHeader("Reason")){
                    console.log("session terminated: Bye locally!");
                } else {
                    console.log("session terminated: Bye remotely[" + request.getHeader("Reason") + "]");
                }

                context.state = PHONE_STATE.TERMINATED;

                if (undefined != options["onStatus"]){
                    options.onStatus(context.state, SESSION_END_REASON.NORMAL, context.direction);
                }
            }).on('cancel', function(){
                console.log("session canceled");
            }).on('terminated', function(message, cause){
                console.log("session terminated");

                context.session = null;
                context.direction = SESSION_DIRECTION.INVALID;
                context.state = PHONE_STATE.IDLE;
                
                if (undefined != options["onStatus"]){
                    options.onStatus(context.state, "", context.direction);
                }
            }).on('trackAdded', function(){
                console.log("session trackAdded");

                var pc = context.session.sessionDescriptionHandler.peerConnection;

                // Gets remote tracks
                var receivers = pc.getReceivers();
                if (receivers.length) { // If we don't have receivers, don't do anything
                    var remoteStream = new MediaStream();
                    receivers.forEach(function(receiver){
                        console.log("add receiver trace: " + receiver.track);
                        remoteStream.addTrack(receiver.track);
                    });

                    var remoteAudioElement = document.getElementById(options.remoteAudio);
                    remoteAudioElement.srcObject = remoteStream;
                    remoteAudioElement.play();
                }
            });
        };

        this.register = function(){
            if (userAgent.isRegistered()){
                return;
            }

            userAgent.start();
        };

        this.unregister = function(){
            // userAgent.unregister();
            userAgent.stop();
        };

        this.offhook = function(){
            if (PHONE_STATE.INVALID == context.state){
                console.log("Not registered yet, the feature is not available!");

                return;
            } else if (PHONE_STATE.IDLE == context.state){
                context.session = userAgent.invite(arguments[0] ? arguments[0] : "0", {
                    //inviteWithoutSdp: true,
                    rel100: SIP.C.supported.REQUIRED,
                    sessionDescriptionHandlerOptions: {
                        constraints: {
                            audio: true,
                            video: false
                        }
                    }
                });

                context.direction = SESSION_DIRECTION.OUTBOUND;
                addSessionEventListeners();
            } else if (PHONE_STATE.RINGING == context.state){
                if (SESSION_DIRECTION.INBOUND == context.direction){
                   context.session && context.session.accept({
					   rel100: SIP.C.supported.REQUIRED,
					   sessionDescriptionHandlerOptions: {
						   constraints: {
							   audio: true,
							   video: false
						   }
					   }
				   });
                }
            } else if (PHONE_STATE.ACCEPTED == context.state){
                console.log("There is already a session in progress, please end it first!");

                return;
            } else if (PHONE_STATE.TERMINATED == context.state){
                console.log("Unexpected to reach here!");

                return;
            }
        };

        this.onhook = function(){
            if (PHONE_STATE.INVALID == context.state){
            } else if (PHONE_STATE.IDLE == context.state){
            } else if (PHONE_STATE.RINGING == context.state){
                context.session && context.session.terminate();
            } else if (PHONE_STATE.ACCEPTED == context.state){
                context.session && context.session.terminate();
            } else if (PHONE_STATE.TERMINATED == context.state){
            }
        };
    };

    !('xSoftphone' in window) && (window.xSoftphone = xSoftphone);
}(window));